import copy
import datetime
import logging
import os

import jwt
import ujson
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.domain.SettleDetailInfo import SettleDetailInfo
from alipay.aop.api.domain.SettleInfo import SettleInfo
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from django.core.cache import caches
from django.db.models import Prefetch, Q
from django.db.transaction import atomic
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django_filters.rest_framework import DjangoFilterBackend
from django_redis import get_redis_connection
from rest_framework.decorators import api_view, action
from rest_framework.filters import OrderingFilter
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from api.consts import MAX_PHOTO_SIZE, FILE_UPLOAD_SUCCESS, FILE_SIZE_EXCEEDED, CODE_TOO_FREQUENCY, MOBILE_CODE_SUCCESS, \
    INVALID_TEL_NUM, USER_LOGIN_SUCCESS, USER_LOGIN_FAILED, INVALID_LOGIN_INFO
from api.helpers import EstateFilterSet, HouseInfoFilterSet, \
    LoginRequiredAuthentication, DefaultResponse, RbacPermission
from api.serializers import DistrictSimpleSerializer, DistrictDetailSerializer, \
    AgentCreateSerializer, AgentDetailSerializer, AgentSimpleSerializer, \
    HouseTypeSerializer, TagSerializer, EstateCreateSerializer, \
    EstateDetailSerializer, EstateSimpleSerializer, HouseInfoDetailSerializer, \
    HousePhotoSerializer, HouseInfoCreateSerializer, HouseInfoSimpleSerializer, \
    UserCreateSerializer, UserUpdateSerializer, UserSimpleSerializer
from common.models import District, Agent, HouseType, Tag, User, LoginLog, \
    HousePhoto, Estate, HouseInfo
from common.utils import gen_mobile_code, send_sms_by_luosimao, to_md5_hex, \
    get_ip_address, upload_stream_to_qiniu
from common.validators import check_tel, check_username, check_email
from izufang.settings import SECRET_KEY

# 配置日志记录器
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(message)s',
                    filemode='a',)
logger = logging.getLogger('')

# 支付宝配置对象
alipay_client_config = AlipayClientConfig()
# 支付宝分配给开发者的应用ID
alipay_client_config.app_id = '2016101800716377'
alipay_client_config.server_url = 'https://openapi.alipaydev.com/gateway.do'
# App私钥（密钥生成工具中生成的私钥，可以在生成的文件中找到）
alipay_client_config.app_private_key = 'MIIEowIBAAKCAQEA1NPx89jxynhQF38Qot1QUW1duHErHY3vGZgCek0GErdIKfgjJ93gHLWvKEuUEauPDW1tgXbo6+qa39OdyFQ8838g2jP519gxywHUYGgR0TKY8TlDYogII2xboY/6lPKuGTZKCd7s0UF5gZ5VxAAo7QlF8PajwlQDqD+TJPbp/tUnhAsJlqh/iLldL3SdEcdUkQ6n2Edt5/w1G8y91XbtiRc4m5hccDAmzaBMBAfzlbtc0ZwZA0A8AtdBovXgl6LjARh4OzRIg0dH4mnPOvhym70Nq+THV9Ul6gJqufyVQ+OGjJRI/OUASqonDW2h0aUGwc0/8NrWgj15kwv8/+S9fwIDAQABAoIBADohGXiszH2sltOUFQsmv4U+BdcWsdwEpEWtSx/0YbBC9ybfa1q8MzYkOY8b9XlODmhwdvRhcgTdsydnOTU6LNuk4Wg3wgm+NvtnqIYcZqFo3HUmWs1SskZaljxugtaj4gFo3pgLl0sgQvuwL2S1VnhHjB7gfqHZKJaPprT167t6JNVPY9y6CcA7cc+KG06Fjz9HtN0K6sICIIsCfHvncOA1vuAL2esp8V1mNASo9E8NXtVyITu7/z0Lrgr4qZP/CVfh6LCLa3UfOdJLo5eVTwGn7i5ATAllOxdfXiLTt34cCfEePCsoet87/Ye1xFiq+VICFmzOaX4k/zmALZ/bDKECgYEA+u/mAvARI00S27kQ9+FwojXddeQqSzrufNHbjbowi41zctl8+yt4z2uw9XqQwM/Ugw1uuKwTdQEB2tNdbkA9mkZyYPQsiLlofIx0dRe596FBcvHOrn5BwmA/Vyd7Ju2+U28EePXkhwgTgv9o6O25H+gEPTBtTMjIyESRbNK+r20CgYEA2R81/+YQQoPFXjzXKX+6qzGA4cvX6w7OpEAPK+Vxyp54QFOaEJBYbYftJNcyFCrY+sqguVPW3hqP9zhNYjMeKDGmQR4gO84QsZItGgUj9OaXEOJ++z/1Jer6blPzwQgB9aivmePPecdwTSl82wIFncihtBu3wmiHqmfxvL1qERsCgYAQ555oLkIJTcTqdnI1MMPEubo8me1behHc6MpQpu1kSVgwsYQg65HM72VRdDtSMe4QEhSWbkk1RacZVcnihoNf5EUKUQi7ATqPwWqLBNkrXHqrQqz4xlqpvJAnz+oU+kkrF7yGZZJKonmmBozLvFAPXTMD6EJEvPYBv9TILv5XlQKBgAoRb3CZW4GqGJnhGl0bRi2wEvjlefK1chGdwtZXQmqR5KOe/NLtzpvtpKqDrBfRUNR4VvGhGKKd+rUNEFGQa0KT4tC8M+RyDsYdMCg3us4dbz3iMt81vQlwFwLMs83ssCKTGul47eMIw4VgeiCxvE7vwcfXTTMfo8LcJRKKegOnAoGBAPIUmpLK/rQrU13YISil8c4VqTDiY3kZV/ZY/MOHm8ljS8nlqeUpbfglF5Y7Jgf6Cl7dP2nKAKoFSGulE/eTDz+wMrU+FCSxXf6wtgurfLuWl9sgLC5euCfYxI+kqQeRz3Apeh2Bnkc6ct/1liAi7ECZ49rOhfkuXMXddHcPs7pp'
# 支付宝公钥（在网站上可以找到"支付宝公钥"）
alipay_client_config.alipay_public_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB'
# 支付成功返回的页面是自己定制的 备案的域名可以跟服务器IP地址绑定（配置域名解析即可）
# 此处必须写完整的URL
alipay_client_config.return_url = 'https://jackfrued.top/static/html/pay_success.html'
alipay_client_config.notify_url = 'https://jackfrued.top/api/get_alipay_notification/'

# 支付宝客户端对象
alipay_client = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)


@api_view(('GET', ))
def get_payment_page(request, houseid):
    # https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay
    model = AlipayTradePagePayModel()
    # 产品订单号
    model.out_trade_no = '20200305164652000001'
    # 订单总金额
    model.total_amount = 200
    # 订单主题
    model.subject = '租房订金'
    # 销售产品码，与支付宝签约的产品码名称。 注：目前仅支持FAST_INSTANT_TRADE_PAY
    model.product_code = 'FAST_INSTANT_TRADE_PAY'
    # 结算详细信息
    settle_detail_info = SettleDetailInfo()
    settle_detail_info.amount = 200
    # 结算收款方的账户类型
    settle_detail_info.trans_in_type = 'cardAliasNo'
    # 结算收款方
    settle_detail_info.trans_in = '6216603100004601162'
    settle_detail_infos = list()
    settle_detail_infos.append(settle_detail_info)
    # 结算信息
    settle_info = SettleInfo()
    settle_info.settle_detail_infos = settle_detail_infos
    model.settle_info = settle_info
    # sub_merchant = SubMerchant()
    # sub_merchant.merchant_id = '2088102180149774'
    # model.sub_merchant = sub_merchant
    request = AlipayTradePagePayRequest(biz_model=model)
    url = alipay_client.page_execute(request, http_method='GET')
    # 此处应该补充生成交易流水的代码 ---> Model
    # 将用户信息、房源信息、交易编号、交易金额、交易时间、交易状态、……写入数据库交易流水表

    # 还需要一个设计一个接口作为接收通知的接口其中包括对订单交易结果的查询
    # 最后还要更新之前的交易流水信息
    # request = AlipayTradeQueryRequest(biz_model=model)
    # content = alipay_client.page_execute(request)
    return DefaultResponse(data={'url': url})


@api_view(('POST', ))
def upload_house_photo(request):
    file_obj = request.FILES.get('mainphoto')
    if file_obj and len(file_obj) < MAX_PHOTO_SIZE:
        prefix = to_md5_hex(file_obj.file)
        filename = f'{prefix}{os.path.splitext(file_obj.name)[1]}'
        photo = HousePhoto()
        photo.path = filename
        photo.ismain = True
        photo.save()
        upload_stream_to_qiniu(copy.copy(file_obj.file), filename, len(file_obj))
        resp = DefaultResponse(*FILE_UPLOAD_SUCCESS, data={
            'photoid': photo.photoid,
            'url': f'http://qea6re99o.bkt.clouddn.com/{photo.path}'
        })
    else:
        resp = DefaultResponse(*FILE_SIZE_EXCEEDED)
    return resp


@api_view(('GET', ))
def get_code_by_sms(request, tel):
    """获取短信验证码"""
    if check_tel(tel):
        if caches['default'].get(f'{tel}:block'):
            resp = DefaultResponse(*CODE_TOO_FREQUENCY)
        else:
            code = gen_mobile_code()
            message = f'您的短信验证码是{code}，打死也不能告诉别人哟！【Python小课】'
            # 异步化的执行函数（把函数放到消息队列中去执行）----> 消息的生产者
            send_sms_by_luosimao.delay(tel, message)
            caches['default'].set(f'{tel}:block', code, timeout=120)
            caches['default'].set(f'{tel}:valid', code, timeout=600)
            resp = DefaultResponse(*MOBILE_CODE_SUCCESS)
    else:
        resp = DefaultResponse(*INVALID_TEL_NUM)
    return resp


@api_view(('POST', ))
def login(request):
    """登录（获取用户身份令牌）"""
    username = request.data.get('username')
    password = request.data.get('password')
    if (check_username(username) or check_tel(username) or
            check_email(username)) and len(password) >= 6:
        password = to_md5_hex(password)
        q = Q(username=username, password=password) | \
            Q(tel=username, password=password) | \
            Q(email=username, password=password)
        user = User.objects.filter(q).only('username', 'realname').first()
        if user:
            # 用户登录成功通过JWT生成用户身份令牌
            payload = {
                'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
                'data': {
                    'userid': user.userid,
                }
            }
            token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode()
            with atomic():
                current_time = timezone.now()
                if not user.lastvisit or \
                        (current_time - user.lastvisit).days >= 1:
                    user.point += 2
                    user.lastvisit = current_time
                    user.save()
                loginlog = LoginLog()
                loginlog.user = user
                loginlog.logdate = current_time
                loginlog.ipaddr = get_ip_address(request)
                loginlog.save()
            resp = DefaultResponse(*USER_LOGIN_SUCCESS, data={
                'token': token, 'username': user.username, 'realname': user.realname
            })
        else:
            resp = DefaultResponse(*USER_LOGIN_FAILED)
    else:
        resp = DefaultResponse(*INVALID_LOGIN_INFO)
    return resp


@api_view(('GET', ))
@cache_page(timeout=365 * 86400)
def get_provinces(request):
    """获取省级行政单位"""
    queryset = District.objects.filter(parent__isnull=True).only('name')
    serializer = DistrictSimpleSerializer(queryset, many=True)
    return Response({'results': serializer.data})


@api_view(('GET', ))
# @authentication_classes((LoginRequiredAuthentication, ))
# @permission_classes((RbacPermission, ))
def get_district(request, distid):
    """获取地区详情"""
    redis_cli = get_redis_connection()
    data = redis_cli.get(f'izufang:district:{distid}')
    if data:
        data = ujson.loads(data)
    else:
        district = District.objects.filter(distid=distid).defer('parent').first()
        data = DistrictDetailSerializer(district).data
        redis_cli.set(f'izufang:district:{distid}', ujson.dumps(data), ex=900)
    return Response(data)


@method_decorator(decorator=cache_page(timeout=86400), name='get')
class HotCityView(ListAPIView):
    """热门城市视图
    get:
        获取热门城市
    """
    queryset = District.objects.filter(ishot=True).only('name')
    serializer_class = DistrictSimpleSerializer
    pagination_class = None


@method_decorator(decorator=cache_page(timeout=120), name='list')
@method_decorator(decorator=cache_page(timeout=300), name='retrieve')
class AgentViewSet(ModelViewSet):
    """经理人视图
    list:
        获取经理人列表
    retrieve:
        获取经理人详情
    create:
        创建经理人
    update:
        更新经理人信息
    partial_update:
        更新经理人信息
    delete:
        删除经理人
    """
    queryset = Agent.objects.all()

    def get_queryset(self):
        name = self.request.GET.get('name')
        if name:
            self.queryset = self.queryset.filter(name__contains=name)
        servstar = self.request.GET.get('servstar')
        if servstar:
            self.queryset = self.queryset.filter(servstar__gte=servstar)
        if self.action == 'list':
            self.queryset = self.queryset.only('name', 'tel', 'servstar')
        else:
            self.queryset = self.queryset.prefetch_related(
                Prefetch('estates',
                         queryset=Estate.objects.all().only('name').order_by('-hot'))
            )
        return self.queryset.order_by('-servstar')

    def get_serializer_class(self):
        if self.action in ('create', 'update'):
            return AgentCreateSerializer
        return AgentDetailSerializer if self.action == 'retrieve' \
            else AgentSimpleSerializer


@method_decorator(decorator=cache_page(timeout=86400), name='list')
@method_decorator(decorator=cache_page(timeout=86400), name='retrieve')
class HouseTypeViewSet(ModelViewSet):
    """户型视图集"""
    queryset = HouseType.objects.all()
    serializer_class = HouseTypeSerializer
    pagination_class = None


@method_decorator(decorator=cache_page(timeout=3600), name='list')
class TagViewSet(ModelViewSet):
    """房源标签视图集"""
    queryset = Tag.objects.all()
    serializer_class = TagSerializer


@method_decorator(decorator=cache_page(timeout=300), name='list')
@method_decorator(decorator=cache_page(timeout=300), name='retrieve')
class EstateViewSet(ModelViewSet):
    """楼盘视图集"""
    queryset = Estate.objects.all()
    filter_backends = (DjangoFilterBackend, OrderingFilter)
    # 只能做精确查询，而且多个条件间只能是而且的关系
    # filter_fields = ('name', 'hot', 'district')
    filterset_class = EstateFilterSet
    ordering = '-hot'
    ordering_fields = ('district', 'hot', 'name')
    authentication_classes = (LoginRequiredAuthentication, )
    permission_classes = (RbacPermission, )

    def get_queryset(self):
        if self.action == 'list':
            queryset = self.queryset.only('name', 'hot')
        else:
            queryset = self.queryset\
                .defer('district__parent', 'district__ishot', 'district__intro')\
                .select_related('district')
        return queryset

    def get_serializer_class(self):
        if self.action in ('create', 'update'):
            return EstateCreateSerializer
        return EstateDetailSerializer if self.action == 'retrieve' \
            else EstateSimpleSerializer


@method_decorator(decorator=cache_page(timeout=120), name='list')
@method_decorator(decorator=cache_page(timeout=300), name='retrieve')
class HouseInfoViewSet(ModelViewSet):
    """房源视图集"""
    queryset = HouseInfo.objects.all()
    serializer_class = HouseInfoDetailSerializer
    filter_backends = (DjangoFilterBackend, OrderingFilter)
    filterset_class = HouseInfoFilterSet
    ordering = ('-pubdate', )
    ordering_fields = ('pubdate', 'price')

    @action(methods=('GET', ), detail=True)
    def photos(self, request, pk):
        queryset = HousePhoto.objects.filter(house=self.get_object())
        return Response(HousePhotoSerializer(queryset, many=True).data)

    def get_queryset(self):
        if self.action == 'list':
            return self.queryset\
                .only('houseid', 'title', 'area', 'floor', 'totalfloor', 'price',
                      'mainphoto', 'priceunit', 'street', 'type',
                      'district_level3__distid', 'district_level3__name')\
                .select_related('district_level3', 'type')\
                .prefetch_related('tags')
        return self.queryset\
            .defer('user', 'district_level2',
                   'district_level3__parent', 'district_level3__ishot', 'district_level3__intro',
                   'estate__district', 'estate__hot', 'estate__intro',
                   'agent__realstar', 'agent__profstar', 'agent__certificated')\
            .select_related('district_level3', 'type', 'estate', 'agent')\
            .prefetch_related('tags')

    def get_serializer_class(self):
        if self.action in ('create', 'update'):
            return HouseInfoCreateSerializer
        return HouseInfoDetailSerializer if self.action == 'retrieve' \
            else HouseInfoSimpleSerializer


class UserViewSet(ModelViewSet):
    """用户模型视图集"""
    queryset = User.objects.all()

    def get_serializer_class(self):
        if self.action == 'create':
            return UserCreateSerializer
        elif self.action == 'update':
            return UserUpdateSerializer
        return UserSimpleSerializer
